package com.shanzmoo.stm.admin.web;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.shanzmoo.base.bean.SHttpResponse;
import com.shanzmoo.base.constant.Constant;
import com.shanzmoo.base.constant.ErrorCode;
import com.shanzmoo.base.exception.AuthException;
import com.shanzmoo.core.auth.LoginReq;
import com.shanzmoo.core.manager.JwtManager;
import com.shanzmoo.core.util.JwtUtils;
import com.shanzmoo.core.util.RedisCoreKeyUtils;
import com.shanzmoo.db.store.entity.StoreMemberEntity;
import com.shanzmoo.stm.admin.domain.bo.AuthStoreMemberBO;
import com.shanzmoo.stm.admin.domain.bo.StoreMemberAuthBO;
import com.shanzmoo.stm.admin.manager.SysUserManager;
import com.shanzmoo.stm.admin.util.RedisKeyUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
@Api(tags = "系统登录")
@RestController
@RequestMapping("/auth")
public class LoginController {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtManager jwtManager;
    @Autowired
    private SysUserManager sysUserManager;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @ApiOperation(value = "登录")
    @PostMapping("/login")
    public SHttpResponse<StoreMemberAuthBO> login(@Valid @RequestBody LoginReq loginReq) {
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(loginReq.getUsername(), loginReq.getPassword()));
        SecurityContextHolder.getContext().setAuthentication(authentication);

        AuthStoreMemberBO authUser = JSON.parseObject(JSON.toJSONString(authentication.getPrincipal()), AuthStoreMemberBO.class);

        // 生成登录 token 并存储到 redis中
        String jwt = jwtManager.createJWT(false, authUser.getUserId(), authUser.getUsername(),
                authUser.getRoleIds(), authUser.getAuthorities(), RedisKeyUtils.installSubectJwtKey(authUser.getUsername()));

        StoreMemberEntity user = sysUserManager.getUserByName(loginReq.getUsername());

        if (null == user) {
            SHttpResponse.fail(ErrorCode.NOT_AUTH_BADCREDENTIALS);
        }

        StoreMemberAuthBO userVo = new StoreMemberAuthBO();
        userVo.setUserId(user.getId());
        userVo.setUsername(user.getMobile());
        userVo.setToken("Bearer " + jwt);

        return SHttpResponse.ok(userVo);
    }


    @ApiOperation(value = "退出")
    @PostMapping("/logout")
    public SHttpResponse<Boolean> logout() {
        try {
            // 从redis中删除jwt信息
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                    .getRequestAttributes()).getRequest();

            String jwt = JwtUtils.getJwtFromRequest(request);
            Assert.isTrue(StrUtil.isNotEmpty(jwt), "token获取失败");

            Claims claims = JwtUtils.getClaimsFromJwt(jwt);

            String redisKey = RedisKeyUtils.installSubectJwtKey(claims.getSubject());
            // 校验redis中的JWT是否存在
            Long expire = stringRedisTemplate.getExpire(redisKey, TimeUnit.MILLISECONDS);
            if (Objects.isNull(expire) || expire <= 0) {
                throw new AuthException("Token 不存在或已过期");
            }
            // 校验redis中的JWT是否与当前的一致，不一致则代表用户已注销/用户在不同设备登录，均代表JWT已过期
            String redisToken = stringRedisTemplate.opsForValue()
                    .get(redisKey);
            if (!StrUtil.equals(jwt, redisToken)) {
                throw new AuthException("Token 信息异常");
            }
            stringRedisTemplate.delete(redisKey);
        } catch (SecurityException e) {
            return SHttpResponse.fail(Constant.ERRCODE_LOGIC, "退出异常");
        }
        return SHttpResponse.ok();
    }
}
